#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <poll.h>
#include <xcb/xcb.h>
#include "event.h"
#include "window.h"
#include "types.h"

xcb_connection_t *dpy;
static xcb_screen_t *scr;
static xcb_window_t window;
static xcb_visualid_t visual;
static xcb_colormap_t colormap;

static xcb_gcontext_t gc_draw;
static xcb_gcontext_t gc_clear;
static xcb_gcontext_t gc_attr;

int x=0, y=0, w=1000, h = 20;
bool topbar = true;

uint32_t bg = 0x66228888, fg=0x66222288, ul=0x6600FF00;

xcb_visualid_t get_visual (void)
{
    xcb_depth_iterator_t iter;
    iter = xcb_screen_allowed_depths_iterator(scr);
    // Try to find a RGBA visual
    while (iter.rem) {
        xcb_visualtype_t *vis = xcb_depth_visuals(iter.data);
        if (iter.data->depth == 32)
            return vis->visual_id;
        xcb_depth_next(&iter);
    }
    // Fallback to the default one
    return scr->root_visual;
}

bool check_connection(xcb_connection_t *conn)
{
    int xerr;
    if ((xerr = xcb_connection_has_error(conn)) != 0) {
        printf("The server closed the connection: ");
        switch (xerr) {
            case XCB_CONN_ERROR:
                printf("socket, pipe or stream error.\n");
                break;
            case XCB_CONN_CLOSED_EXT_NOTSUPPORTED:
                printf("unsupported extension.\n");
                break;
            case XCB_CONN_CLOSED_MEM_INSUFFICIENT:
                printf("not enough memory.\n");
                break;
            case XCB_CONN_CLOSED_REQ_LEN_EXCEED:
                printf("request length exceeded.\n");
                break;
            case XCB_CONN_CLOSED_PARSE_ERR:
                printf("can't parse display string.\n");
                break;
            case XCB_CONN_CLOSED_INVALID_SCREEN:
                printf("invalid screen.\n");
                break;
            case XCB_CONN_CLOSED_FDPASSING_FAILED:
                printf("failed to pass FD.\n");
                break;
            default:
                printf("unknown error.\n");
                break;
        }
        return false;
    } else {
        return true;
    }
}

int main()
{
    // xconn
    dpy = xcb_connect(NULL, NULL);
    if(xcb_connection_has_error(dpy)) {
        printf("can't connect to X\n");
        return -1;
    }
    scr = xcb_setup_roots_iterator(xcb_get_setup(dpy)).data;
    visual = get_visual();
    colormap = xcb_generate_id(dpy);
    xcb_create_colormap(dpy, XCB_COLORMAP_ALLOC_NONE, colormap, scr->root, visual);


    // init
    // monitor_new
    int depth = (visual == scr->root_visual) ? XCB_COPY_FROM_PARENT : 32;
    printf("depth=%d\n", depth);
    window = xcb_generate_id(dpy);
    xcb_create_window(dpy, depth, window, scr->root,
            x, y, w, h, 0 /* border width*/,
            XCB_WINDOW_CLASS_INPUT_OUTPUT, visual,
            // --- mask
            XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT |
            XCB_CW_EVENT_MASK | XCB_CW_COLORMAP,
            // --- value
            (const uint32_t []) {
            bg, bg, false, /* false: takes place, no border */
            XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS, colormap 
            });
    xcb_pixmap_t pixmap = xcb_generate_id(dpy);
    xcb_create_pixmap(dpy, depth, pixmap, window, w, h);

    monitor_t monitor = {
        .window = window,
        .x = 0,
        .y = 0,
        .w = 400,
        .h = 20,
    };
    setup_ewmh(&monitor);
    // set_ewmh_atoms();
#if 1
    gc_draw  = xcb_generate_id(dpy);
    gc_clear = xcb_generate_id(dpy);
    gc_attr  = xcb_generate_id(dpy);

    xcb_create_gc(dpy, gc_draw,  pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ fg });
    xcb_create_gc(dpy, gc_clear, pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ bg });
    xcb_create_gc(dpy, gc_attr,  pixmap, XCB_GC_FOREGROUND, (const uint32_t []){ ul });


    //xcb_change_gc(dpy, gc_draw,  XCB_GC_FOREGROUND, (const uint32_t []){ fg });
    //xcb_change_gc(dpy, gc_clear, XCB_GC_FOREGROUND, (const uint32_t []){ bg });
    //xcb_change_gc(dpy, gc_attr,  XCB_GC_FOREGROUND, (const uint32_t []){ ul });

    // draw.
    xcb_poly_fill_rectangle(dpy, pixmap, gc_clear, 1, 
            (const xcb_rectangle_t []){ { x, y, w, h } });
    xcb_map_window(dpy, window);
    // Make the bar visible and clear the pixmap
    // for (monitor_t *mon = monhead; mon; mon = mon->next) {
    //fill_rect(mon->pixmap, gc_clear, 0, 0, mon->width, bh); // XXX

    // Make sure that the window really gets in the place it's supposed to be
    // Some WM such as Openbox need this
    xcb_configure_window(dpy, window, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, \
            (const uint32_t []){ x, y });

    // Set the WM_NAME atom to the user specified value
    char *wm_name = "bsphaha";
    if (wm_name)
        xcb_change_property(dpy, XCB_PROP_MODE_REPLACE, window, XCB_ATOM_WM_NAME,
                XCB_ATOM_STRING, 8 ,strlen(wm_name), wm_name);
    // }
#endif


    xcb_flush(dpy); //
#if 0
    xcb_poly_fill_rectangle(dpy, pixmap, gc_draw, 1, 
            (const xcb_rectangle_t []){ { x, y, w, h } });
    xcb_poly_fill_rectangle(dpy, pixmap, gc_clear, 1, 
            (const xcb_rectangle_t []){ { x, y, w, h } });
    xcb_poly_fill_rectangle(dpy, pixmap, gc_attr, 1, 
            (const xcb_rectangle_t []){ { x, y, w, h } });
#endif
    /*
       xcb_copy_area(dpy, pixmap, window, gc_draw, 0, 0, 0, 0, w, h);
       xcb_map_window(dpy, window);
       xcb_flush(dpy); //
       while(1);
       */

    struct pollfd fds[1];    

    fds[0].fd       = xcb_get_file_descriptor(dpy);
    fds[0].events   = POLLIN;    

    xcb_generic_event_t *event;    

    for(;;) {    
        xcb_flush(dpy);    

        // TODO: polling ...    
        if(poll(fds, 1, -1) > 0) {     
            if(fds[0].revents & POLLIN) {    
                while ((event = xcb_poll_for_event(dpy)) != NULL) {    
                    handle_event(event);    
                    free(event);    
                }    
            }    
        }    

        if (!check_connection(dpy)) {    
            printf("check conn got false!\n");
            break;
        }    
    }
}

